#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include "Fifo.hpp"

using namespace std;

const string gpath = "/home/ls/linux"; // 公共路径
int gprojId = 0x666;                   // 随意设置
int gshmsize = 4096;
const mode_t gmode = 0600;

// 转16进制
std::string ToHex(key_t k)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%x", k);
    return buffer;
}

class SharedMemory
{
private:
    void CreateShmHelper(int shmflg)
    {
        // 1. 创建key
        key_t k = ::ftok(gpath.c_str(), gprojId);
        if (k < 0) // ftok 失败，会返回-1，并设置错误码
            cerr << "ftok failed" << endl;
        // 2. 创建共享内存并获取
        // 注意：共享内存也有权限 gmode
        int shmid = ::shmget(k, gshmsize, shmflg);
        if (shmid < 0) // 如果返回值小于0，说明创建失败
            cerr << "shnget failed" << endl;
        cout << "shmid：" << shmid << endl;
    }

public:
    SharedMemory()
        :_shmid(-1)
        ,_key(0)
        ,_addr(nullptr)
    {}
    // 创建共享内存，需要 IPC_CREAT 和 IPC_EXCL一起，因为不存在要新建
    void CreateShm()
    {
        if(_shmid == -1)  // 没有创建过 shm
            CreateShmHelper(IPC_CREAT | IPC_EXCL | gmode);
    }

    // 获取共享内存，只需要获取，不存在不要新建，所以和上面的区别就是 shmflg 的区别，所以代码重复，可以设置同一份
    void GetShm()
    {
        CreateShmHelper(IPC_CREAT);
    }

    // 挂接 shm
    void AttachShm()
    {
        _addr = shmat(_shmid, nullptr, 0); // 成功返回共享内存块的起始空间，失败返回-1
        if ((long long)_addr == -1)             // 这里要转成long long，因为在64位机器上指针是8字节，而ret也需要8字节，不然会精度缺失
            cerr << "attach failed" << endl;
    }

    // 去关联 shm
    void DetachShm()
    {
        if(_addr != nullptr)
            ::shmdt(_addr);
    }

    // 删除 shm
    void DeleteShm()
    {
        shmctl(_shmid, IPC_RMID, nullptr);
    }

    void* GetAddr()
    {
        return _addr;
    }
private:
    key_t _key;
    int _shmid;
    void* _addr;
};

SharedMemory shm;
Fifo gpipe;

struct data
{
    char status[32];
    char lasttime[48];
    char image[4000];
};